/* parser */

#include "bparser.h"
#include "bmem.h"

void initProto(Proto* p) {
	p->code = bf_newvec(opcode, MAXCODESIZE);
	p->codelen = 0;
	p->error = 0;
	p->loop_p = -1;
}

void addcode(Proto* p, Token_t t, int extra) {
	if(p->codelen == MAXCODESIZE - 1) {
		p->error++;
		bf_error("Error: Code too long.");
		p->t = tok2dat(EOS, 0);		/* Stop reading */
		return;
	}
	p->code[p->codelen].t = t;
	p->code[p->codelen].extra = extra;
	p->codelen++;
}

/*
Proto* testProto(Proto* p, LState* l) {
	save(p, l);
	addcode(p, ADD, 0);
	addcode(p, EOS, 0);
	char errorbuff[50];
	sprintf(errorbuff,"Error: A 'char' or an 'int'"
	        " required in line %d.\n", l->linenum);
	p->error++;
//	bf_error(errorbuff);
	return p;
}
*/

Proto* parse(Proto* p, LState* l) {
	save(p, l);
	while(p->t.t != EOS) {
//		printf("len:%d, tok:%d\n", p->codelen, p->t.t);
		switch(p->t.t) {
			case MOV:
			case ADD:
			case SET: {
				Token_t t = p->t.t;
				save(p, l);
				if(p->t.t == NUM) {
					addcode(p, t, p->t.extra);
					if(p->t.t != EOS) {
						save(p, l);
					}
				} else {
					addcode(p, t, t <= ADD);	/* default : ADD:1, MOV:1, SET:0*/
				}
				break;
			}
			case INPUT:
			case OUTPUT: {
				Token_t t = p->t.t;
				save(p, l);
				if(p->t.t == CHAR || p->t.t == INT) {
					addcode(p, t, p->t.t == CHAR);
					if(p->t.t != EOS) {
						save(p, l);
					}
				} else {
					char errorbuff[50];
					sprintf(errorbuff,"Error: A 'char' or an 'int'"
					        " required in line %d.\n", l->linenum);
					p->error++;
					bf_error(errorbuff);
				}
				break;
			}
			case LOOP: {
				addcode(p, LOOP, p->loop_p);
				p->loop_p = p->codelen - 1;
				save(p, l);
				break;
			}
			case END: {
				if(p->loop_p == -1) {
					char errorbuff[50];
					sprintf(errorbuff, "Error: Unexpected 'end' in line %d.\n",
					        l->linenum);
					bf_error(errorbuff);
					save(p, l);
					break;
				}
				addcode(p, END, p->loop_p);
				p->loop_p = p->code[p->loop_p].extra;
				p->code[p->code[p->codelen - 1].extra].extra = p->codelen - 1;
				save(p, l);
				break;
			}
			case CHAR: {
				char errorbuff[50];
				sprintf(errorbuff, "Error: 'char' should follow a 'read' or"
				        " a 'write' in line %d.\n", l->linenum);
				bf_error(errorbuff);
				save(p, l);
				break;
			}
			case INT: {
				char errorbuff[50];
				sprintf(errorbuff, "Error: 'int' should follow a 'read' or"
				        " a 'write' in line %d.\n", l->linenum);
				bf_error(errorbuff);
				save(p, l);
				break;
			}
			case NUM: {
				char errorbuff[50];
				sprintf(errorbuff, "Error: numbers should follow a 'mov', an 'add'"
				        " or a 'set' in line %d.\n", l->linenum);
				bf_error(errorbuff);
				save(p, l);
				break;
			}
		}
	}
	addcode(p, EOS, 0);
	while(p->loop_p != -1) {
		char errorbuff[30];
		sprintf(errorbuff, "Error: loop never ends.\n");
		bf_error(errorbuff);
		p->loop_p = p->code[p->loop_p].extra;
	}
	p->error += l->errors;
	return p;
}
